/*
 * Copyright 2007-2009 Thomas Krause (thomas@berlin-reloaded.de)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.gidoo.cms.publisher;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.IAjaxIndicatorAware;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.authorization.strategies.role.annotations.AuthorizeInstantiation;
import org.apache.wicket.extensions.ajax.markup.html.AjaxIndicatorAppender;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.markup.html.CSSPackageResource;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.ExternalLink;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;
import org.gidoo.cms.GidooCMS;
import org.gidoo.cms.IPlugin;
import org.gidoo.cms.admin.AdminPage;
import org.gidoo.cms.editor.edit.PreviewPageCreator;
import org.gidoo.cms.helper.AdminNavPanel;
import org.gidoo.cms.helper.ResourceDownloadLink;
import org.gidoo.cms.helper.Tools;
import org.gidoo.cms.jcr.PublishDataManager;
import org.gidoo.cms.jcr.JcrHelper;
import org.gidoo.cms.login.GidooCMSSession;
import org.gidoo.wickettools.XHTMLDiff;
import org.gidoo.wickettools.MessageBox;

/**
 *
 * @author <a href="mailto:thomas@berlin-reloaded.de">Thomas Krause</a>
 */
@AuthorizeInstantiation(GidooCMSSession.ROLE_PUBLISHER)
public class PublisherPage extends WebPage implements IAjaxIndicatorAware
{

  private FeedbackPanel feedback;
  private ListView<String> lsvPublishPages;
  private ListView<String> lsvPublishResources;
  private List<String> pathsForPagesToPublish;
  private List<String> pathsForResourcesToPublish;
  private GidooCMSSession theSession;
  private ModalWindow window;
  
  private ListView<IPlugin> lsvPlugins;

  private final AjaxIndicatorAppender indicatorAppender = new AjaxIndicatorAppender();

  /** Creates a new instance of PublisherPage */
  public PublisherPage()
  {
    try
    {
      add(CSSPackageResource.getHeaderContribution(AdminPage.CSS_ADMIN));
      add(indicatorAppender);

      theSession = (GidooCMSSession) this.getSession();

      feedback = new FeedbackPanel("feedbackPanel");
      feedback.setOutputMarkupId(true);
      add(feedback);

      window = new ModalWindow("window");
      window.setUseInitialHeight(true);
      window.setInitialWidth(700);
      window.setInitialHeight(500);
      add(window);

      add(new Label("lblTitle", new ResourceModel("publisherPage")));
      add(new Link("lnkLogout")
      {

        public void onClick()
        {
          theSession.signOut();
          this.setResponsePage(((GidooCMS) getApplication()).getHomePage());
        }
      });

      add(new AdminNavPanel("panelAdminNav", PublisherPage.class));
      
      add(new Link("lnkRefresh")
      {

        @Override
        public void onClick()
        {
          setResponsePage(PublisherPage.class);
        }
      });


      Node root = theSession.getJcrRootNode();
      NodeIterator itSections =
        root.getNode("users").getNode(theSession.getUsername()).getNode("publish").getNodes();
      List<String> sectionsOfUser = new LinkedList<String>();
      while(itSections.hasNext())
      {
        sectionsOfUser.add(itSections.nextNode().getName());
      }

      pathsForPagesToPublish = new ArrayList<String>();

      pathsForResourcesToPublish = new ArrayList<String>();

      NodeIterator itDrafts = root.getNode("drafts").getNodes();

      while(itDrafts.hasNext())
      {
        Node n = itDrafts.nextNode();
        try
        {
          boolean isResource = "cms:resource".equals(n.getPrimaryNodeType().getName());
          // find section for this node
          
          String path = Tools.draftName2Path(n.getName());
          String pathForOrig = path;
          if(isResource)
          {
            pathForOrig = pathForOrig.substring(0, pathForOrig.lastIndexOf("/"));
          }
          
          if(root.hasNode(pathForOrig))
          {
          
            Node nOrig = root.getNode(pathForOrig);
            Node s = JcrHelper.getSectionOfNode(nOrig);

            if(s != null && sectionsOfUser.contains(s.getName()))
            {
              String type = n.getPrimaryNodeType().getName();
              if(isResource)
              {
                pathsForResourcesToPublish.add(path);
              }
              else if(type.equals("cms:page") || type.equals("cms:section"))
              {
                pathsForPagesToPublish.add(path);
              }
            }
          }
          else
          {
            warn(getString("draftWithInvalidPath"));
            n.remove();
            root.getNode("drafts").save();
          }
        }
        catch(RepositoryException ex)
        {
          Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
          error(getString("jcrerror"));
        }
      }

      lsvPublishPages = new ListView<String>("lsvPublishPages", pathsForPagesToPublish)
      {

        @Override
        protected void populateItem(ListItem item)
        {
          createPageEntries(item);
        }
      };

      add(lsvPublishPages);

      lsvPublishResources = new ListView<String>("lsvPublishResources", pathsForResourcesToPublish)
      {

        @Override
        protected void populateItem(ListItem item)
        {
          createResourceEntries(item);
        }
      };

      add(lsvPublishResources);

      Label lblNoneResources = new Label("lblNoneResources", new Model(getString("none")));
      lblNoneResources.setVisible(pathsForResourcesToPublish.isEmpty());
      add(lblNoneResources);

      Label lblNonePages = new Label("lblNonePages", new Model(getString("none")));
      lblNonePages.setVisible(pathsForPagesToPublish.isEmpty());
      add(lblNonePages);
    }
    catch(RepositoryException ex)
    {
      Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
      error(getString("jcrerror"));
    }

    lsvPlugins =  new ListView<IPlugin>("lsvPlugins", 
      ((GidooCMS) getApplication()).CMS_CONFIGURATION.getPublishPlugins()) 
    {

      @Override
      protected void populateItem(ListItem item)
      {
        IPlugin p = lsvPlugins.getList().get(item.getIndex());
        item.add(new Label("lblHeading", new Model(p.getPluginName())));
        item.add(p.getPluginPanel("panelPlugin"));
      }
    };
    add(lsvPlugins);
    
  }

  private void createPageEntries(ListItem item)
  {
    try
    {
      String path = pathsForPagesToPublish.get(item.getIndex());

      Node root = ((GidooCMSSession) getSession()).getJcrRootNode();
      Node page = root.getNode("drafts").getNode(Tools.path2DraftName(path));
      Node nOrig = root.getNode(path);
      final String pageOrigURLPath = JcrHelper.getPartialURLForNode(nOrig);
      Node section = JcrHelper.getSectionOfNode(nOrig);


      item.add(new Label("lblPath", new Model(path.substring("sections/".length()))));
      item.add(new Label("lblSection", new Model(section.getName())));
      item.add(new Label("lblEditor", 
        new Model(page.hasProperty("cms:editor") ? 
          page.getProperty("cms:editor").getString() : "" )));

      Calendar c = page.getProperty("jcr:created").getDate();
      Date d = new Date(c.getTimeInMillis());
      item.add(new Label("lblTime", new Model(
        Tools.standardDateFormatter.format(d))));

      final String pagePath = page.getPath().substring(1);
      final PublisherPage finalThis = this;

      item.add(new AjaxLink("lnkPreview")
      {
        @Override
        public void onClick(AjaxRequestTarget target)
        {
          if(pagePath != null)
          {
            try
            {
              String title = "";

              Node page1 =
                      ((GidooCMSSession) getSession()).getJcrRootNode().
                      getNode(pagePath);
              if(page1.hasProperty("cms:title"))
              {
                title = page1.getProperty("cms:title").getString();
              }
              
              window.setPageCreator(new PreviewPageCreator(
                finalThis, page1.getProperty("cms:xml").getString(),
                title, pageOrigURLPath));
              
              window.setTitle(getString("preview") + ": "+ title);
              
              window.show(target);
            }
            catch(RepositoryException ex)
            {
              Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
              error(getString("jcrerror"));
            }
          }
          target.addComponent(feedback);
        }
      });

      item.add(new Link("lnkAccept")
      {

        @Override
        public void onClick()
        {         
          try
          {
            Node myRoot = ((GidooCMSSession) getSession()).getJcrRootNode();
            Node myPage = myRoot.getNode(pagePath);

            PublishDataManager.publishPageDraft(myPage, theSession);
            setResponsePage(PublisherPage.class);
          }
          catch(RepositoryException ex)
          {
            Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
          }
        }
      });

      item.add(new Link("lnkReject")
      {

        @Override
        public void onClick()
        {
          try
          {
            Node page1 = ((GidooCMSSession) getSession()).getJcrRootNode().getNode(pagePath);
            PublishDataManager.rejectPageDraft(page1, theSession);
            setResponsePage(PublisherPage.class);
          }
          catch(RepositoryException ex)
          {
            Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
          }
        }
      });

      item.add(new Link("lnkDiff")
      {

        @Override
        public void onClick()
        {
          try
          {
            Node page1 = 
              ((GidooCMSSession) getSession()).getJcrRootNode().getNode(pagePath);
            showDiff(page1);
          }
          catch(RepositoryException ex)
          {
            Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
            error(getString("jcrerror"));
          }
        }
      });
    }
    catch(RepositoryException ex)
    {
      Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
    }
  }

  private void createResourceEntries(ListItem item)
  {
    try
    {

      String path = pathsForResourcesToPublish.get(item.getIndex());
      Node root = ((GidooCMSSession) getSession()).getJcrRootNode();
      Node drafts = root.getNode("drafts");
      String draftPath = Tools.path2DraftName(path);

      Node res = null;

      if(drafts.hasNode(draftPath))
      {
        res = drafts.getNode(Tools.path2DraftName(path));
      }

      item.add(new Label("lblPath", new Model(path)));
      item.add(new Label("lblType", new Model(res == null ? "null" : res.getProperty("jcr:mimeType").getString())));
      item.add(new Label("lblEditor", new Model(res == null ? "null" : res.getProperty("cms:editor").getString())));

      if(res == null)
      {
        item.add(new Link("lnkShow") {

          @Override
          public void onClick()
          {
            // do nothing
          }
        });
      }
      else
      {
        ResourceDownloadLink lnkShow = new ResourceDownloadLink("lnkShow", res);
        item.add(lnkShow);
      }

      final String resPath = res == null ? null : res.getPath().substring(1);
      
      item.add(new Link("lnkAccept")
      {

        @Override
        public void onClick()
        {
          Session mySession = ((GidooCMSSession) getSession()).getJcrSession();
          Node myRes;
          try
          {
            if(resPath != null)
            {
              myRes = mySession.getRootNode().getNode(resPath);
              publishResource(mySession, myRes);
            }
          }
          catch(RepositoryException ex)
          {
            Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
          }
        }
      });

      item.add(new Link("lnkReject")
      {

        @Override
        public void onClick()
        {
          try
          {
            Node myRes = ((GidooCMSSession) getSession()).getJcrRootNode().getNode(resPath);
            PublishDataManager.rejectResource(myRes, theSession);
            setResponsePage(PublisherPage.class);
            
          }
          catch(RepositoryException ex)
          {
            Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
          }
        }
      });
    }
    catch(RepositoryException ex)
    {
      Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
      error(getString("jcrerror"));
    }

  }

  private void publishResource(Session session, Node res)
  {
    try
    {
      GidooCMSSession gSession = (GidooCMSSession) getSession();
      String path = Tools.draftName2Path(res.getName());
      if(session.getRootNode().hasNode(path))
      {
        // ups, there is already a node with this path, delete the draft and
        // and inform the user
        warn(getString("resexists"));
        Node parent = res.getParent();
        res.remove();
        parent.save();
        return;
      }
      
      res.setProperty("cms:publisher", gSession.getUsername());
      res.setProperty("jcr:lastModified", Calendar.getInstance());
      
      session.move(res.getPath(), "/" + path);
      session.save();
    }
    catch(RepositoryException ex)
    {
      Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
      error(getString("jcrerror"));
    }

    setResponsePage(PublisherPage.class);
  }


  private void showDiff(Node newer) throws RepositoryException
  {
    Node rootNode = ((GidooCMSSession) getSession()).getJcrRootNode();
    String path = Tools.draftName2Path(newer.getName());
    Node old = rootNode.getNode(path);

    String diff = "";

    String titleOld = "";

    if(old == null || 
      (!old.hasProperty("cms:xml") && !old.hasProperty("cms:title"))
      || ("".equals(old.getProperty("cms:xml").getString())
          && "".equals(old.getProperty("cms:title").getString())) )
    {
      diff = XHTMLDiff.doDiff(getString("isnew"), newer.getProperty("cms:xml").getString(),
        getString("old"), getString("new"));
    }
    else
    {
      diff = XHTMLDiff.doDiff(old.getProperty("cms:xml").getString(), 
        newer.getProperty("cms:xml").getString(), getString("old"), getString("new"));
      titleOld = old.getProperty("cms:title").getString();
    }

    diff = wrapDiff( getString("oldtitle"), titleOld, 
      getString("newtitle"), newer.getProperty("cms:title").getString(), diff);

    MessageBox msgbox = new MessageBox(getString("difftitle"), diff, true,
      MessageBox.MessageBoxButtons.OK, this, null);

    this.setResponsePage(msgbox);
  }

  public static String wrapDiff(String oldTitleCaption, String oldTitle,
    String newTitleCaption, String newTitle, String diff)
  {
    return
      "<p>" + oldTitleCaption + oldTitle + "<br />" +
      newTitleCaption + newTitle + "</p>\n" +
      diff;
  }

  public String getAjaxIndicatorMarkupId()
  {
    return indicatorAppender.getMarkupId();
  }
}
